"""
Modified from D-FINE (https://github.com/Peterande/D-FINE)
Copyright (c) 2024 Peterande. All Rights Reserved.
"""

import torch 
import torch.nn as nn 

from datetime import datetime
from pathlib import Path 
from typing import Dict
import atexit

from ..misc import dist_utils
from ..core import BaseConfig


def to(m: nn.Module, device: str):
    if m is None:
        return None 
    return m.to(device) 


def remove_module_prefix(state_dict):
    new_state_dict = {}
    for k, v in state_dict.items():
        if k.startswith('module.'):
            new_state_dict[k[7:]] = v
        else:
            new_state_dict[k] = v
    return new_state_dict


class BaseSolver(object):
    def __init__(self, cfg: BaseConfig) -> None:
        self.cfg = cfg
        self.obj365_ids = [
            0, 46, 5, 58, 114, 55, 116, 65, 21, 40, 176, 127, 249, 24, 56, 139, 92, 78, 99, 96, 
            144, 295, 178, 180, 38, 39, 13, 43, 120, 219, 148, 173, 165, 154, 137, 113, 145, 146, 
            204, 8, 35, 10, 88, 84, 93, 26, 112, 82, 265, 104, 141, 152, 234, 143, 150, 97, 2, 
            50, 25, 75, 98, 153, 37, 73, 115, 132, 106, 61, 163, 134, 277, 81, 133, 18, 94, 30, 
            169, 70, 328, 226
        ]
    def _setup(self):
        """Avoid instantiating unnecessary classes"""
        cfg = self.cfg
        if cfg.device:
            device = torch.device(cfg.device)
        else:
            device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

        self.model = cfg.model

        # NOTE: Must load_tuning_state before EMA instance building
        if self.cfg.tuning:
            print(f'Tuning checkpoint from {self.cfg.tuning}')
            self.load_tuning_state(self.cfg.tuning)

        self.model = dist_utils.warp_model(
            self.model.to(device), sync_bn=cfg.sync_bn, find_unused_parameters=cfg.find_unused_parameters
        )

        self.criterion = self.to(cfg.criterion, device)
        self.postprocessor = self.to(cfg.postprocessor, device)

        self.ema = self.to(cfg.ema, device)
        self.scaler = cfg.scaler

        self.device = device
        self.last_epoch = self.cfg.last_epoch

        self.output_dir = Path(cfg.output_dir)
        self.output_dir.mkdir(parents=True, exist_ok=True)
        self.writer = cfg.writer

        if self.writer:
            atexit.register(self.writer.close)
            if dist_utils.is_main_process():
                self.writer.add_text(f'config', '{:s}'.format(cfg.__repr__()), 0)

    def cleanup(self):
        if self.writer:
            atexit.register(self.writer.close)

    def train(self):
        self._setup()
        self.optimizer = self.cfg.optimizer
        self.lr_scheduler = self.cfg.lr_scheduler
        self.lr_warmup_scheduler = self.cfg.lr_warmup_scheduler

        self.train_dataloader = dist_utils.warp_loader(
            self.cfg.train_dataloader, shuffle=self.cfg.train_dataloader.shuffle
        )
        self.val_dataloader = dist_utils.warp_loader(
            self.cfg.val_dataloader, shuffle=self.cfg.val_dataloader.shuffle
        )

        self.evaluator = self.cfg.evaluator

        # NOTE: Instantiating order
        if self.cfg.resume:
            print(f'Resume checkpoint from {self.cfg.resume}')
            self.load_resume_state(self.cfg.resume)

    def eval(self):
        self._setup()

        self.val_dataloader = dist_utils.warp_loader(
            self.cfg.val_dataloader, shuffle=self.cfg.val_dataloader.shuffle
        )

        self.evaluator = self.cfg.evaluator

        if self.cfg.resume:
            print(f'Resume checkpoint from {self.cfg.resume}')
            self.load_resume_state(self.cfg.resume)

    def to(self, module, device):
        return module.to(device) if hasattr(module, 'to') else module

    def state_dict(self):
        """State dict, train/eval"""
        state = {}
        state['date'] = datetime.now().isoformat()

        # For resume
        state['last_epoch'] = self.last_epoch

        for k, v in self.__dict__.items():
            if hasattr(v, 'state_dict'):
                v = dist_utils.de_parallel(v)
                state[k] = v.state_dict()

        return state

    def load_state_dict(self, state):
        """Load state dict, train/eval"""
        if 'last_epoch' in state:
            self.last_epoch = state['last_epoch']
            print('Load last_epoch')

        for k, v in self.__dict__.items():
            if hasattr(v, 'load_state_dict') and k in state:
                v = dist_utils.de_parallel(v)
                v.load_state_dict(state[k])
                print(f'Load {k}.state_dict')

            if hasattr(v, 'load_state_dict') and k not in state:
                if k == 'ema':
                    model = getattr(self, 'model', None)
                    if model is not None:
                        ema = dist_utils.de_parallel(v)
                        model_state_dict = remove_module_prefix(model.state_dict())
                        ema.load_state_dict({'module': model_state_dict})
                        print(f'Load {k}.state_dict from model.state_dict')
                else:
                    print(f'Not load {k}.state_dict')

    def load_resume_state(self, path: str):
        """Load resume"""
        if path.startswith('http'):
            state = torch.hub.load_state_dict_from_url(path, map_location='cpu')
        else:
            state = torch.load(path, map_location='cpu')
        
        # state['model'] = remove_module_prefix(state['model'])
        self.load_state_dict(state)

    def load_tuning_state(self, path: str):
        """Load model for tuning and adjust mismatched head parameters"""
        if path.startswith('http'):
            state = torch.hub.load_state_dict_from_url(path, map_location='cpu')
        else:
            state = torch.load(path, map_location='cpu')

        module = dist_utils.de_parallel(self.model)

        # Load the appropriate state dict
        if 'ema' in state:
            pretrain_state_dict = state['ema']['module']
        else:
            pretrain_state_dict = state['model']

        # Adjust head parameters between datasets
        try:
            adjusted_state_dict = self._adjust_head_parameters(module.state_dict(), pretrain_state_dict)
            stat, infos = self._matched_state(module.state_dict(), adjusted_state_dict)
        except:
            stat, infos = self._matched_state(module.state_dict(), pretrain_state_dict)

        module.load_state_dict(stat, strict=False)
        print(f'Load model.state_dict, {infos}')

    @staticmethod
    def _matched_state(state: Dict[str, torch.Tensor], params: Dict[str, torch.Tensor]):
        missed_list = []
        unmatched_list = []
        matched_state = {}
        for k, v in state.items():
            if k in params:
                if v.shape == params[k].shape:
                    matched_state[k] = params[k]
                else:
                    unmatched_list.append(k)
            else:
                missed_list.append(k)

        return matched_state, {'missed': missed_list, 'unmatched': unmatched_list}

    def _adjust_head_parameters(self, cur_state_dict, pretrain_state_dict):
        """Adjust head parameters between datasets."""
        # List of parameters to adjust
        if pretrain_state_dict['decoder.denoising_class_embed.weight'].size() != \
                cur_state_dict['decoder.denoising_class_embed.weight'].size():
            del pretrain_state_dict['decoder.denoising_class_embed.weight']

        head_param_names = [
            'decoder.enc_score_head.weight',
            'decoder.enc_score_head.bias'
        ]
        for i in range(8):
            head_param_names.append(f'decoder.dec_score_head.{i}.weight')
            head_param_names.append(f'decoder.dec_score_head.{i}.bias')

        adjusted_params = []

        for param_name in head_param_names:
            if param_name in cur_state_dict and param_name in pretrain_state_dict:
                cur_tensor = cur_state_dict[param_name]
                pretrain_tensor = pretrain_state_dict[param_name]
                adjusted_tensor = self.map_class_weights(cur_tensor, pretrain_tensor)
                if adjusted_tensor is not None:
                    pretrain_state_dict[param_name] = adjusted_tensor
                    adjusted_params.append(param_name)
                else:
                    print(f"Cannot adjust parameter '{param_name}' due to size mismatch.")

        return pretrain_state_dict

    def map_class_weights(self, cur_tensor, pretrain_tensor):
        """Map class weights from pretrain model to current model based on class IDs."""
        if pretrain_tensor.size() == cur_tensor.size():
            return pretrain_tensor

        adjusted_tensor = cur_tensor.clone()
        adjusted_tensor.requires_grad = False

        if pretrain_tensor.size() > cur_tensor.size():
            for coco_id, obj_id in enumerate(self.obj365_ids):
                adjusted_tensor[coco_id] = pretrain_tensor[obj_id+1]
        else:
            for coco_id, obj_id in enumerate(self.obj365_ids):
                adjusted_tensor[obj_id+1] = pretrain_tensor[coco_id]           

        return adjusted_tensor

    def fit(self):
        raise NotImplementedError('')

    def val(self):
        raise NotImplementedError('')

# obj365_classes = [
#         'Person', 'Sneakers', 'Chair', 'Other Shoes', 'Hat', 'Car', 'Lamp', 'Glasses', 
#         'Bottle', 'Desk', 'Cup', 'Street Lights', 'Cabinet/shelf', 'Handbag/Satchel', 
#         'Bracelet', 'Plate', 'Picture/Frame', 'Helmet', 'Book', 'Gloves', 'Storage box', 
#         'Boat', 'Leather Shoes', 'Flower', 'Bench', 'Potted Plant', 'Bowl/Basin', 'Flag', 
#         'Pillow', 'Boots', 'Vase', 'Microphone', 'Necklace', 'Ring', 'SUV', 'Wine Glass', 
#         'Belt', 'Moniter/TV', 'Backpack', 'Umbrella', 'Traffic Light', 'Speaker', 'Watch', 
#         'Tie', 'Trash bin Can', 'Slippers', 'Bicycle', 'Stool', 'Barrel/bucket', 'Van', 
#         'Couch', 'Sandals', 'Bakset', 'Drum', 'Pen/Pencil', 'Bus', 'Wild Bird', 'High Heels', 
#         'Motorcycle', 'Guitar', 'Carpet', 'Cell Phone', 'Bread', 'Camera', 'Canned', 'Truck', 
#         'Traffic cone', 'Cymbal', 'Lifesaver', 'Towel', 'Stuffed Toy', 'Candle', 'Sailboat', 
#         'Laptop', 'Awning', 'Bed', 'Faucet', 'Tent', 'Horse', 'Mirror', 'Power outlet', 
#         'Sink', 'Apple', 'Air Conditioner', 'Knife', 'Hockey Stick', 'Paddle', 'Pickup Truck', 
#         'Fork', 'Traffic Sign', 'Ballon', 'Tripod', 'Dog', 'Spoon', 'Clock', 'Pot', 'Cow', 
#         'Cake', 'Dinning Table', 'Sheep', 'Hanger', 'Blackboard/Whiteboard', 'Napkin', 
#         'Other Fish', 'Orange/Tangerine', 'Toiletry', 'Keyboard', 'Tomato', 'Lantern', 
#         'Machinery Vehicle', 'Fan', 'Green Vegetables', 'Banana', 'Baseball Glove', 
#         'Airplane', 'Mouse', 'Train', 'Pumpkin', 'Soccer', 'Skiboard', 'Luggage', 'Nightstand', 
#         'Tea pot', 'Telephone', 'Trolley', 'Head Phone', 'Sports Car', 'Stop Sign', 'Dessert', 
#         'Scooter', 'Stroller', 'Crane', 'Remote', 'Refrigerator', 'Oven', 'Lemon', 'Duck', 
#         'Baseball Bat', 'Surveillance Camera', 'Cat', 'Jug', 'Broccoli', 'Piano', 'Pizza', 
#         'Elephant', 'Skateboard', 'Surfboard', 'Gun', 'Skating and Skiing shoes', 'Gas stove', 
#         'Donut', 'Bow Tie', 'Carrot', 'Toilet', 'Kite', 'Strawberry', 'Other Balls', 'Shovel', 
#         'Pepper', 'Computer Box', 'Toilet Paper', 'Cleaning Products', 'Chopsticks', 'Microwave', 
#         'Pigeon', 'Baseball', 'Cutting/chopping Board', 'Coffee Table', 'Side Table', 'Scissors', 
#         'Marker', 'Pie', 'Ladder', 'Snowboard', 'Cookies', 'Radiator', 'Fire Hydrant', 'Basketball', 
#         'Zebra', 'Grape', 'Giraffe', 'Potato', 'Sausage', 'Tricycle', 'Violin', 'Egg', 
#         'Fire Extinguisher', 'Candy', 'Fire Truck', 'Billards', 'Converter', 'Bathtub', 
#         'Wheelchair', 'Golf Club', 'Briefcase', 'Cucumber', 'Cigar/Cigarette ', 'Paint Brush', 
#         'Pear', 'Heavy Truck', 'Hamburger', 'Extractor', 'Extention Cord', 'Tong', 
#         'Tennis Racket', 'Folder', 'American Football', 'earphone', 'Mask', 'Kettle', 
#         'Tennis', 'Ship', 'Swing', 'Coffee Machine', 'Slide', 'Carriage', 'Onion', 
#         'Green beans', 'Projector', 'Frisbee', 'Washing Machine/Drying Machine', 'Chicken', 
#         'Printer', 'Watermelon', 'Saxophone', 'Tissue', 'Toothbrush', 'Ice cream', 
#         'Hotair ballon', 'Cello', 'French Fries', 'Scale', 'Trophy', 'Cabbage', 'Hot dog', 
#         'Blender', 'Peach', 'Rice', 'Wallet/Purse', 'Volleyball', 'Deer', 'Goose', 'Tape', 
#         'Tablet', 'Cosmetics', 'Trumpet', 'Pineapple', 'Golf Ball', 'Ambulance', 'Parking meter', 
#         'Mango', 'Key', 'Hurdle', 'Fishing Rod', 'Medal', 'Flute', 'Brush', 'Penguin', 
#         'Megaphone', 'Corn', 'Lettuce', 'Garlic', 'Swan', 'Helicopter', 'Green Onion', 
#         'Sandwich', 'Nuts', 'Speed Limit Sign', 'Induction Cooker', 'Broom', 'Trombone', 
#         'Plum', 'Rickshaw', 'Goldfish', 'Kiwi fruit', 'Router/modem', 'Poker Card', 'Toaster', 
#         'Shrimp', 'Sushi', 'Cheese', 'Notepaper', 'Cherry', 'Pliers', 'CD', 'Pasta', 'Hammer', 
#         'Cue', 'Avocado', 'Hamimelon', 'Flask', 'Mushroon', 'Screwdriver', 'Soap', 'Recorder', 
#         'Bear', 'Eggplant', 'Board Eraser', 'Coconut', 'Tape Measur/ Ruler', 'Pig', 
#         'Showerhead', 'Globe', 'Chips', 'Steak', 'Crosswalk Sign', 'Stapler', 'Campel', 
#         'Formula 1 ', 'Pomegranate', 'Dishwasher', 'Crab', 'Hoverboard', 'Meat ball', 
#         'Rice Cooker', 'Tuba', 'Calculator', 'Papaya', 'Antelope', 'Parrot', 'Seal', 
#         'Buttefly', 'Dumbbell', 'Donkey', 'Lion', 'Urinal', 'Dolphin', 'Electric Drill', 
#         'Hair Dryer', 'Egg tart', 'Jellyfish', 'Treadmill', 'Lighter', 'Grapefruit', 
#         'Game board', 'Mop', 'Radish', 'Baozi', 'Target', 'French', 'Spring Rolls', 'Monkey', 
#         'Rabbit', 'Pencil Case', 'Yak', 'Red Cabbage', 'Binoculars', 'Asparagus', 'Barbell', 
#         'Scallop', 'Noddles', 'Comb', 'Dumpling', 'Oyster', 'Table Teniis paddle', 
#         'Cosmetics Brush/Eyeliner Pencil', 'Chainsaw', 'Eraser', 'Lobster', 'Durian', 'Okra', 
#         'Lipstick', 'Cosmetics Mirror', 'Curling', 'Table Tennis '
# ]

# coco_classes = ['person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus',
#                'train', 'truck', 'boat', 'traffic light', 'fire hydrant',
#                'stop sign', 'parking meter', 'bench', 'wild bird', 'cat', 'dog',
#                'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', 'giraffe',
#                'backpack', 'umbrella', 'handbag/satchel', 'tie', 'luggage', 'frisbee',
#                'skating and skiing shoes', 'snowboard', 'baseball', 'kite', 'baseball bat',
#                'baseball glove', 'skateboard', 'surfboard', 'tennis racket',
#                'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl/basin',
#                'banana', 'apple', 'sandwich', 'orange/tangerine', 'broccoli', 'carrot',
#                'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch',
#                'potted plant', 'bed', 'dinning table', 'toilet', 'moniter/tv', 'laptop',
#                'mouse', 'remote', 'keyboard', 'cell phone', 'microwave',
#                'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock',
#                'vase', 'scissors', 'stuffed toy', 'hair dryer', 'toothbrush']

'''
obj365_classes = [
    (0, 'Person'),
    (1, 'Sneakers'),
    (2, 'Chair'),
    (3, 'Other Shoes'),
    (4, 'Hat'),
    (5, 'Car'),
    (6, 'Lamp'),
    (7, 'Glasses'),
    (8, 'Bottle'),
    (9, 'Desk'),
    (10, 'Cup'),
    (11, 'Street Lights'),
    (12, 'Cabinet/shelf'),
    (13, 'Handbag/Satchel'),
    (14, 'Bracelet'),
    (15, 'Plate'),
    (16, 'Picture/Frame'),
    (17, 'Helmet'),
    (18, 'Book'),
    (19, 'Gloves'),
    (20, 'Storage box'),
    (21, 'Boat'),
    (22, 'Leather Shoes'),
    (23, 'Flower'),
    (24, 'Bench'),
    (25, 'Potted Plant'),
    (26, 'Bowl/Basin'),
    (27, 'Flag'),
    (28, 'Pillow'),
    (29, 'Boots'),
    (30, 'Vase'),
    (31, 'Microphone'),
    (32, 'Necklace'),
    (33, 'Ring'),
    (34, 'SUV'),
    (35, 'Wine Glass'),
    (36, 'Belt'),
    (37, 'Monitor/TV'),
    (38, 'Backpack'),
    (39, 'Umbrella'),
    (40, 'Traffic Light'),
    (41, 'Speaker'),
    (42, 'Watch'),
    (43, 'Tie'),
    (44, 'Trash bin Can'),
    (45, 'Slippers'),
    (46, 'Bicycle'),
    (47, 'Stool'),
    (48, 'Barrel/bucket'),
    (49, 'Van'),
    (50, 'Couch'),
    (51, 'Sandals'),
    (52, 'Basket'),
    (53, 'Drum'),
    (54, 'Pen/Pencil'),
    (55, 'Bus'),
    (56, 'Wild Bird'),
    (57, 'High Heels'),
    (58, 'Motorcycle'),
    (59, 'Guitar'),
    (60, 'Carpet'),
    (61, 'Cell Phone'),
    (62, 'Bread'),
    (63, 'Camera'),
    (64, 'Canned'),
    (65, 'Truck'),
    (66, 'Traffic cone'),
    (67, 'Cymbal'),
    (68, 'Lifesaver'),
    (69, 'Towel'),
    (70, 'Stuffed Toy'),
    (71, 'Candle'),
    (72, 'Sailboat'),
    (73, 'Laptop'),
    (74, 'Awning'),
    (75, 'Bed'),
    (76, 'Faucet'),
    (77, 'Tent'),
    (78, 'Horse'),
    (79, 'Mirror'),
    (80, 'Power outlet'),
    (81, 'Sink'),
    (82, 'Apple'),
    (83, 'Air Conditioner'),
    (84, 'Knife'),
    (85, 'Hockey Stick'),
    (86, 'Paddle'),
    (87, 'Pickup Truck'),
    (88, 'Fork'),
    (89, 'Traffic Sign'),
    (90, 'Balloon'),
    (91, 'Tripod'),
    (92, 'Dog'),
    (93, 'Spoon'),
    (94, 'Clock'),
    (95, 'Pot'),
    (96, 'Cow'),
    (97, 'Cake'),
    (98, 'Dining Table'),
    (99, 'Sheep'),
    (100, 'Hanger'),
    (101, 'Blackboard/Whiteboard'),
    (102, 'Napkin'),
    (103, 'Other Fish'),
    (104, 'Orange/Tangerine'),
    (105, 'Toiletry'),
    (106, 'Keyboard'),
    (107, 'Tomato'),
    (108, 'Lantern'),
    (109, 'Machinery Vehicle'),
    (110, 'Fan'),
    (111, 'Green Vegetables'),
    (112, 'Banana'),
    (113, 'Baseball Glove'),
    (114, 'Airplane'),
    (115, 'Mouse'),
    (116, 'Train'),
    (117, 'Pumpkin'),
    (118, 'Soccer'),
    (119, 'Skiboard'),
    (120, 'Luggage'),
    (121, 'Nightstand'),
    (122, 'Tea pot'),
    (123, 'Telephone'),
    (124, 'Trolley'),
    (125, 'Head Phone'),
    (126, 'Sports Car'),
    (127, 'Stop Sign'),
    (128, 'Dessert'),
    (129, 'Scooter'),
    (130, 'Stroller'),
    (131, 'Crane'),
    (132, 'Remote'),
    (133, 'Refrigerator'),
    (134, 'Oven'),
    (135, 'Lemon'),
    (136, 'Duck'),
    (137, 'Baseball Bat'),
    (138, 'Surveillance Camera'),
    (139, 'Cat'),
    (140, 'Jug'),
    (141, 'Broccoli'),
    (142, 'Piano'),
    (143, 'Pizza'),
    (144, 'Elephant'),
    (145, 'Skateboard'),
    (146, 'Surfboard'),
    (147, 'Gun'),
    (148, 'Skating and Skiing Shoes'),
    (149, 'Gas Stove'),
    (150, 'Donut'),
    (151, 'Bow Tie'),
    (152, 'Carrot'),
    (153, 'Toilet'),
    (154, 'Kite'),
    (155, 'Strawberry'),
    (156, 'Other Balls'),
    (157, 'Shovel'),
    (158, 'Pepper'),
    (159, 'Computer Box'),
    (160, 'Toilet Paper'),
    (161, 'Cleaning Products'),
    (162, 'Chopsticks'),
    (163, 'Microwave'),
    (164, 'Pigeon'),
    (165, 'Baseball'),
    (166, 'Cutting/chopping Board'),
    (167, 'Coffee Table'),
    (168, 'Side Table'),
    (169, 'Scissors'),
    (170, 'Marker'),
    (171, 'Pie'),
    (172, 'Ladder'),
    (173, 'Snowboard'),
    (174, 'Cookies'),
    (175, 'Radiator'),
    (176, 'Fire Hydrant'),
    (177, 'Basketball'),
    (178, 'Zebra'),
    (179, 'Grape'),
    (180, 'Giraffe'),
    (181, 'Potato'),
    (182, 'Sausage'),
    (183, 'Tricycle'),
    (184, 'Violin'),
    (185, 'Egg'),
    (186, 'Fire Extinguisher'),
    (187, 'Candy'),
    (188, 'Fire Truck'),
    (189, 'Billiards'),
    (190, 'Converter'),
    (191, 'Bathtub'),
    (192, 'Wheelchair'),
    (193, 'Golf Club'),
    (194, 'Briefcase'),
    (195, 'Cucumber'),
    (196, 'Cigar/Cigarette'),
    (197, 'Paint Brush'),
    (198, 'Pear'),
    (199, 'Heavy Truck'),
    (200, 'Hamburger'),
    (201, 'Extractor'),
    (202, 'Extension Cord'),
    (203, 'Tong'),
    (204, 'Tennis Racket'),
    (205, 'Folder'),
    (206, 'American Football'),
    (207, 'Earphone'),
    (208, 'Mask'),
    (209, 'Kettle'),
    (210, 'Tennis'),
    (211, 'Ship'),
    (212, 'Swing'),
    (213, 'Coffee Machine'),
    (214, 'Slide'),
    (215, 'Carriage'),
    (216, 'Onion'),
    (217, 'Green Beans'),
    (218, 'Projector'),
    (219, 'Frisbee'),
    (220, 'Washing Machine/Drying Machine'),
    (221, 'Chicken'),
    (222, 'Printer'),
    (223, 'Watermelon'),
    (224, 'Saxophone'),
    (225, 'Tissue'),
    (226, 'Toothbrush'),
    (227, 'Ice Cream'),
    (228, 'Hot Air Balloon'),
    (229, 'Cello'),
    (230, 'French Fries'),
    (231, 'Scale'),
    (232, 'Trophy'),
    (233, 'Cabbage'),
    (234, 'Hot Dog'),
    (235, 'Blender'),
    (236, 'Peach'),
    (237, 'Rice'),
    (238, 'Wallet/Purse'),
    (239, 'Volleyball'),
    (240, 'Deer'),
    (241, 'Goose'),
    (242, 'Tape'),
    (243, 'Tablet'),
    (244, 'Cosmetics'),
    (245, 'Trumpet'),
    (246, 'Pineapple'),
    (247, 'Golf Ball'),
    (248, 'Ambulance'),
    (249, 'Parking Meter'),
    (250, 'Mango'),
    (251, 'Key'),
    (252, 'Hurdle'),
    (253, 'Fishing Rod'),
    (254, 'Medal'),
    (255, 'Flute'),
    (256, 'Brush'),
    (257, 'Penguin'),
    (258, 'Megaphone'),
    (259, 'Corn'),
    (260, 'Lettuce'),
    (261, 'Garlic'),
    (262, 'Swan'),
    (263, 'Helicopter'),
    (264, 'Green Onion'),
    (265, 'Sandwich'),
    (266, 'Nuts'),
    (267, 'Speed Limit Sign'),
    (268, 'Induction Cooker'),
    (269, 'Broom'),
    (270, 'Trombone'),
    (271, 'Plum'),
    (272, 'Rickshaw'),
    (273, 'Goldfish'),
    (274, 'Kiwi Fruit'),
    (275, 'Router/Modem'),
    (276, 'Poker Card'),
    (277, 'Toaster'),
    (278, 'Shrimp'),
    (279, 'Sushi'),
    (280, 'Cheese'),
    (281, 'Notepaper'),
    (282, 'Cherry'),
    (283, 'Pliers'),
    (284, 'CD'),
    (285, 'Pasta'),
    (286, 'Hammer'),
    (287, 'Cue'),
    (288, 'Avocado'),
    (289, 'Hami Melon'),
    (290, 'Flask'),
    (291, 'Mushroom'),
    (292, 'Screwdriver'),
    (293, 'Soap'),
    (294, 'Recorder'),
    (295, 'Bear'),
    (296, 'Eggplant'),
    (297, 'Board Eraser'),
    (298, 'Coconut'),
    (299, 'Tape Measure/Ruler'),
    (300, 'Pig'),
    (301, 'Showerhead'),
    (302, 'Globe'),
    (303, 'Chips'),
    (304, 'Steak'),
    (305, 'Crosswalk Sign'),
    (306, 'Stapler'),
    (307, 'Camel'),
    (308, 'Formula 1'),
    (309, 'Pomegranate'),
    (310, 'Dishwasher'),
    (311, 'Crab'),
    (312, 'Hoverboard'),
    (313, 'Meatball'),
    (314, 'Rice Cooker'),
    (315, 'Tuba'),
    (316, 'Calculator'),
    (317, 'Papaya'),
    (318, 'Antelope'),
    (319, 'Parrot'),
    (320, 'Seal'),
    (321, 'Butterfly'),
    (322, 'Dumbbell'),
    (323, 'Donkey'),
    (324, 'Lion'),
    (325, 'Urinal'),
    (326, 'Dolphin'),
    (327, 'Electric Drill'),
    (328, 'Hair Dryer'),
    (329, 'Egg Tart'),
    (330, 'Jellyfish'),
    (331, 'Treadmill'),
    (332, 'Lighter'),
    (333, 'Grapefruit'),
    (334, 'Game Board'),
    (335, 'Mop'),
    (336, 'Radish'),
    (337, 'Baozi'),
    (338, 'Target'),
    (339, 'French'),
    (340, 'Spring Rolls'),
    (341, 'Monkey'),
    (342, 'Rabbit'),
    (343, 'Pencil Case'),
    (344, 'Yak'),
    (345, 'Red Cabbage'),
    (346, 'Binoculars'),
    (347, 'Asparagus'),
    (348, 'Barbell'),
    (349, 'Scallop'),
    (350, 'Noodles'),
    (351, 'Comb'),
    (352, 'Dumpling'),
    (353, 'Oyster'),
    (354, 'Table Tennis Paddle'),
    (355, 'Cosmetics Brush/Eyeliner Pencil'),
    (356, 'Chainsaw'),
    (357, 'Eraser'),
    (358, 'Lobster'),
    (359, 'Durian'),
    (360, 'Okra'),
    (361, 'Lipstick'),
    (362, 'Cosmetics Mirror'),
    (363, 'Curling'),
    (364, 'Table Tennis')
]
'''
